package com.lf.mykotlin.classes

import com.lf.mykotlin.classes.innerclass.InnerClassDemo
import com.lf.mykotlin.classes.innerclass.NormalClassDemo
import com.lf.mykotlin.classes.person.Person

// 类可以有一个 主构造器，以及一个或多个次构造器，主构造器是类头部的一部分，位于类名称之后:
class MyKotlinClass(text: String, index :Int) {


    /*
        Kotlin中的类可详细的分为：
            密封类 sealed
                ，1. 密封类的本质就是抽象类
                  2. 密封类与普通抽象类的区别在于：
                        密封类的子类是固定的。
                        密封类的子类必须与密封类本身在同一个文件中，在其他文件中则不能为密封类派生子类，这样就限制了在其他文件中派生子类。
                        但密封类的间接子类（子类的子类）则无须在同 个文件中
            内部类（嵌套类）inner
                  1. 使用 inner 修饰的嵌套类叫内部类，相当于 Java 中无 static修饰的非静态内部类
            抽象类
            枚举类
            接口类 interface
            数据类
     */

    /*

        1. 构造函数：
            1. 在 Kotlin 中的一个类可以有一个主构造函数以及一个或多个次构造函数。
               主构造函数是类头的一部分：它跟在类名与可选的类型参数后。

                class MyKotlinClass constructor(firstName: String) { /*……*/ }

            2. 如果主构造函数没有任何注解或者可见性修饰符（public、private、protected和internal），可以省略这个 constructor 关键字。

                class MyKotlinClass(text: String, index :Int) { /*……*/ }

                //有注解，不可以省略
                class Example public @Inject constructor(name: String) { … }

            3. 如果一个非抽象类没有声明构造函数(主构造函数或次构造函数)，
               那么该类会有一个默认的没有参数 的主构造函数，而且它是由public类型修饰的。
               如果你不想你的类有公共的构造函数，你就得声明一个空的主构造函数：

                class DontCreateMe private constructor () { }

            4. 主构造器中不能包含任何代码，初始化代码可以放在初始化代码段中，初始化代码段使用 init 关键字作为前缀。
               在实例初始化期间，初始化块按照它们出现在类体中的顺序执行，与属性初始化器交织在一起：
               主构造器的参数可以在初始化代码段中使用，也可以在类主体n定义的属性初始化代码中使用。

        2. 类的修饰符
            abstract    // 抽象类
            final       // 类不可继承，默认属性
            enum        // 枚举类
            open        // 类可继承，类默认是final的
            annotation  // 注解类

        3. 类的访问权限修饰符
            private    // 仅在同一个文件中可见
            protected  // 同一个文件中或子类可见
            public     // 所有调用的地方都可见
            internal   // 同一个模块中可见
     */


    // 1. 定义属性
    var name : String? = "zhangsan"
    var age = index
    val sex = "男"
    var textContent = text

    var loginCode : String = ""
            get() = "123"
            set(value) {field = value}

    // 即isEmpty这个属性，是判断该类的size属性是否等于0
    val isEmpty : Boolean
        get() = this.age == 0

    // 另一个例子
    val num = 2
        get() = if (field > 5) 10 else 0


    /*
        2. 关键字：init{…}
                主构造函数初始化
                能使用构造函数中的参数
     */
    init {
        println("Init block")
    }

    /*
        3. 次构造函数
             如果类有主构造函数，每个次构造函数都要直接或间接通过另一个次构造函数代理主构造函数。
             在同一个类中代理另一个构造函数使用 this 关键字：
             通过次构造函数实例化对象时会优先 执行初始化块中的方法
     */
    constructor (name2: String, age2:Int, sex: Boolean) : this(name2, age2) {
        this.age = age2
        this.name = name2
    }


    /*
        4. 函数的扩展
            扩展函数是静态解析的，并不是接收者类型的虚拟成员，在调用扩展函数时，具体被调用的的是哪一个函数，
            由调用函数的的对象表达式来决定的，而不是动态的类型决定的:
            若扩展函数和成员函数一致，则使用该函数时，会优先使用成员函数。
            要使用所定义包之外的一个扩展, 通过import导入扩展的函数名进行使用:
     */
    fun work() {

    }


    /*
        5. 对象表达式 关键字 object
            object[: 0~N个父类型］ {
                ／／对象表达式的类体部分
            }

           对象声明
            object ObjectName[: 0~N个父类型］｛
                ／／对象表达式的类体部分
            }

            1. 对象表达式是一个表达式，因此它可以被赋值给变量 而对象声明不是表达式，因此它不能用于赋值。
            2. 对象声明可包含嵌套类，不能包含内部类 而对象表达式可包含内部类，不能包含嵌套类。
            3. 对象声明不能定义在函数和方法内 但对象表达式可嵌套在其他对象声明或非内部类中。

     */
    fun testObject() {
        val obj = object {
            var a = 10;
            var b = 20;
        }
        println("a + b = ${obj.a + obj.b}")


        var clickListener = object : ClickListener {
            override fun onClick() {

            }
        }


        // 公有函数，返回类型是Any
        fun publicFoo() = object { val x: String = "x" }

        fun bar() {
            val x2 = publicFoo().x
        }

    }

    interface ClickListener {
        fun onClick();
    }

    /*
        6. 伴生对象
            用来实现java中的静态类成员变量 使用关键字companion object
            伴生对象中定义的成员可以通过类名直接访问
            调用伴生对象时还可以省略伴生对象的名称，这时候直接使 用类名. Companion的方式来完成调用
            一个类只能有一个伴生对象
     */
    companion object {
        fun create() : Person = Person("zhangsna");
    }


    fun tempCompanion() {
        
        // 调用伴生对象方法
        val person = MyKotlinClass.create()

        // 为伴生对象扩展方法
        fun MyKotlinClass.Companion.show() {
            println("为伴生对象扩展方法")
        }
    }


    /*
        7. 委托
            有类委托和属性委托两种。 Kotlin通过关键字by来实现委托。
            类委托， 就是在一个类中定义的方法，实际是调用另一个类对象的方法来实现 的
     */


    /*
        8. 内部类
            内部类可以嵌套多层
            普通嵌套类没有持有外部类的引用 ，所以是无法访问外部类变量的
     */
    fun testInnerClass() {

        // 普通嵌套类
        val one = NormalClassDemo.Outer().one
        val  two = NormalClassDemo.Outer.Nested().getTwo()
        val three = NormalClassDemo.Outer.Nested.Three().getThre()


        // 内部类
        val sum = InnerClassDemo.Outer().Inner().sum()
    }
}